package chaincode

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/hyperledger/fabric-contract-api-go/v2/contractapi"
)

// SmartContract provides functions for managing a Form
type SmartContract struct {
	contractapi.Contract
}

// CreateForm issues a new form to the world state with given details.
func (s *SmartContract) CreateForm(ctx contractapi.TransactionContextInterface, key string, form string) error {
	exists, err := s.FormExists(ctx, key)
	if err != nil {
		return err
	}
	if exists {
		return fmt.Errorf("the form %s already exists", key)
	}

	return ctx.GetStub().PutState(key, []byte(form))
}

// ReadForm returns the form stored in the world state with given id.
func (s *SmartContract) ReadForm(ctx contractapi.TransactionContextInterface, key string) (string, error) {
	form, err := ctx.GetStub().GetState(key)
	if err != nil {
		return "failed", fmt.Errorf("failed to read from world state: %v", err)
	}
	if form == nil {
		return "not exists", fmt.Errorf("the form %s does not exist", key)
	}

	return string(form), nil
}

// ReadFormHistoryForKey returns the form history in the ledger with given id.
func (s *SmartContract) ReadFormHistoryForKey(ctx contractapi.TransactionContextInterface, key string) (string, error) {
	historyIterator, err := ctx.GetStub().GetHistoryForKey(key)
	if err != nil {
		return "get history error", err
	}
	defer historyIterator.Close()

	var history = make(map[string]interface{})
	for historyIterator.HasNext() {
		queryResponse, err := historyIterator.Next()
		if err != nil {
			return "iterator error", err
		}

		history[queryResponse.GetTimestamp().AsTime().Format(time.RFC3339)] = string(queryResponse.GetValue())
	}

	historyJson, err := json.Marshal(history)
	if err != nil {
		return "json marshal error", err
	}

	return string(historyJson), nil
}

// UpdateForm updates an existing form in the world state with provided parameters.
func (s *SmartContract) UpdateForm(ctx contractapi.TransactionContextInterface, key string, form string) error {
	exists, err := s.FormExists(ctx, key)
	if err != nil {
		return err
	}
	if !exists {
		return fmt.Errorf("the form %s does not exist", key)
	}

	// overwriting original form with new form
	return ctx.GetStub().PutState(key, []byte(form))
}

// DeleteForm deletes a given form from the world state.
func (s *SmartContract) DeleteForm(ctx contractapi.TransactionContextInterface, key string) error {
	exists, err := s.FormExists(ctx, key)
	if err != nil {
		return err
	}
	if !exists {
		return fmt.Errorf("the form %s does not exist", key)
	}

	return ctx.GetStub().DelState(key)
}

// FormExists returns true when form with given ID exists in world state
func (s *SmartContract) FormExists(ctx contractapi.TransactionContextInterface, key string) (bool, error) {
	formJSON, err := ctx.GetStub().GetState(key)
	if err != nil {
		return false, fmt.Errorf("failed to read from world state: %v", err)
	}

	return formJSON != nil, nil
}

// ReadAllForms returns all forms found in world state
func (s *SmartContract) ReadFormsByRange(ctx contractapi.TransactionContextInterface, keyStart string, keyEnd string) (string, error) {
	// range query with empty string for startKey and endKey does an
	// open-ended query of all forms in the chaincode namespace.
	resultsIterator, err := ctx.GetStub().GetStateByRange(keyStart, keyEnd)
	if err != nil {
		return "get by range error", err
	}
	defer resultsIterator.Close()

	var forms = make(map[string]interface{})
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return "iterator error", err
		}

		forms[queryResponse.GetKey()] = string(queryResponse.GetValue())
	}

	formsJson, err := json.Marshal(forms)
	if err != nil {
		return "json marshal error", err
	}

	return string(formsJson), nil
}
